package com.xyy.saas.payment.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.xyy.saas.payment.constants.Constants;
import com.xyy.saas.payment.constants.HttpStatus;
import com.xyy.saas.payment.cores.vo.QueryFuminAccountVo;
import com.xyy.saas.payment.dao.model.FbankPayRecords;
import com.xyy.saas.payment.dao.model.FbankPayRecordsQueryParam;
import com.xyy.saas.payment.enums.BusinessType;
import com.xyy.saas.payment.enums.TradeTypeEnum;
import com.xyy.saas.payment.pojo.bo.PlatformAccountBo;
import com.xyy.saas.payment.manage.cores.vo.ResultPageVo;
import com.xyy.saas.payment.pojo.vo.TradeCheckQueryParamVo;
import com.xyy.saas.payment.pojo.vo.TradeCheckVo;
import com.xyy.saas.payment.service.FbankPayRecordsService;
import com.xyy.saas.payment.service.PlatformAccountsManage;
import com.xyy.saas.payment.service.TradeCheckService;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.DateUtil;
import com.xyy.saas.payment.util.ExportExcelUtil;
import com.xyy.saas.payment.util.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 *
 */
@Slf4j
@Service
@DependsOn("apolloUtil")
public class TradeCheckServiceImpl implements TradeCheckService, InitializingBean {
    @Value("${fbankPayRecords.archive.endTime:2024-01-01}")
    private String fbankPayRecordsArchiveEndTime;
    @Autowired
    private FbankPayRecordsService fbankPayRecordsService;

    @Autowired
    private PlatformAccountsManage platformAccountsManage;

    private List<PlatformAccountBo> platformAccountBos = JSONUtils.parse2List(ApolloUtil.getProperty("platform_accounts"), PlatformAccountBo.class);

    /**
     * 结果使用
     */
    private SimpleDateFormat sdfResult = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public ResultPageVo<List<TradeCheckVo>> queryByPage(String jsonStr) {
        ResultPageVo result = new ResultPageVo();
        try {


            if (StringUtils.isNotEmpty(jsonStr)) {
                TradeCheckQueryParamVo paramVo = JSONObject.parseObject(jsonStr, TradeCheckQueryParamVo.class);
                //将前台参数转换成规范格式,并对参数做校验
                FbankPayRecordsQueryParam param = checkParam(paramVo,result,"query");

                Date archiveDate = DateUtil.string2Date(fbankPayRecordsArchiveEndTime, DateUtil.DATEFORMATDAY);
                param.setIsArchive(param.getPayEndTime().before(archiveDate) ? 1 : 0);
                if (0 == result.getCode()){
                    //查询出总数
                    Long total = fbankPayRecordsService.getTotal(param);
                    if ((null == total || 0 == total) && null == result){
                        result = ResultPageVo.createSuccess();
                    }else if (0 < total){
                        List<FbankPayRecords> reports = fbankPayRecordsService.pageAllByParam(param);
                        List<TradeCheckVo> vos = poToVo(reports);
                        result = ResultPageVo.createSuccess(param.getPage(),param.getPageSize(),total,vos);
                    }
                }
            } else {
                result = ResultPageVo.createFail("未检测到入参,请检查");
            }

        }catch (Exception e){
            log.error("查询交易对账信息异常:{}",e);
            result = ResultPageVo.createFail("查询异常,请联系管理员");
        }
        return result;
    }

    @Override
    public void export(String jsonStr, HttpServletRequest request,
                                   HttpServletResponse response) {
        ResultPageVo pageVo = null;
        try {
            if (StringUtils.isNotEmpty(jsonStr)) {
                TradeCheckQueryParamVo paramVo = JSONObject.parseObject(jsonStr, TradeCheckQueryParamVo.class);
                //将前台参数转换成规范格式,并对参数做校验
                FbankPayRecordsQueryParam param = checkParam(paramVo,pageVo,"export");

                Date archiveDate = DateUtil.string2Date(fbankPayRecordsArchiveEndTime, DateUtil.DATEFORMATDAY);
                param.setIsArchive(param.getPayEndTime().before(archiveDate) ? 1 : 0);
                if (null == pageVo){
                    //查询出总数
                    Long total = fbankPayRecordsService.getTotal(param);
                    if (300000 < total){
                        log.error("一次导出最多不能超过30W条数据");
                        response.setStatus(HttpStatus.DATA_MAX);
                        return;
                    }else if (0 <= total){
                        //查询数据
                        List<FbankPayRecords> reports = fbankPayRecordsService.pageAllByParam(param);
                        List<TradeCheckVo> vos = poToVo(reports);
                        //拼装文件名
                        String filname = buildFileName(param);

                        //导出数据
                        ExportExcelUtil.createExcelDateFormat(response, request, filname, "交易对账表", Constants.TRADE_CHECK_HEADER, Constants.TRADE_CHECK_FILED, vos, true, null);
                    }
                }else {
                    response.setStatus(HttpStatus.PARAM_ERROR);
                    return;
                }
            }

        }catch (Exception e){
            log.error("查询交易对账信息异常:{}",e);
            response.setStatus(HttpStatus.SYSTEM_BUSY);
            return;
        }
    }

    //导出不需要分页,但是单次最多导出10W条数据
    private FbankPayRecordsQueryParam checkParam(TradeCheckQueryParamVo paramVo,ResultPageVo result,String type){
        FbankPayRecordsQueryParam param = new FbankPayRecordsQueryParam();

        if ((0 == paramVo.getPage() || 0 == paramVo.getPageSize()) && !"export".equals(type)){
            result.setFail("分页参数错误,请检查");
            return null;
        }

        try {
            //一天的毫秒数
            int dayMis=1000*60*60*24;
            Date payStartTime = StringUtils.isBlank(paramVo.getPayStartTime()) ? null : new Date(Long.valueOf(paramVo.getPayStartTime()));
            Date payEndDate = StringUtils.isBlank(paramVo.getPayEndTime()) ? null : new Date(Long.valueOf(paramVo.getPayEndTime()));

            if(null != payEndDate) {
                //查询条件结束时间变为最后结束时间当天的最后一秒
                Long endMill = payEndDate.getTime();
                Long lastMill = endMill + (dayMis - 1);
                payEndDate = new Date(lastMill);
            }

            if(null != payStartTime && null != payEndDate) {
                //查询的时间范围不能超过31天
                Calendar start = Calendar.getInstance();
                Calendar end = Calendar.getInstance();
                start.setTime(payStartTime);
                start.add(Calendar.DATE, 31);
                end.setTime(payEndDate);

                if (start.before(end)) {
                    result.setFail("查询时间超过31天");
                    return null;
                }
            }

            param.setBusinessOrderType(paramVo.getBusinessOrderType());
            param.setPayStartTime(payStartTime);
            param.setPayEndTime(payEndDate);
            param.setBusinessType(paramVo.getBusinessType());
            if (!"export".equals(type)) {
                param.setPage(paramVo.getPage());
                param.setPageSize(paramVo.getPageSize());
                param.setOffset((paramVo.getPage() - 1) * paramVo.getPageSize());
            }
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        return param;
    }

    private List<TradeCheckVo>  poToVo(List<FbankPayRecords> pos){
        List<TradeCheckVo> vos = new ArrayList<>();
        if (!CollectionUtils.isEmpty(pos)){
            pos.forEach(po->{
                TradeCheckVo vo = new TradeCheckVo();
                vo.setBusinessType(po.getBusinessType());
                vo.setBusinessTypeName(po.getBusinessType()==null?"":BusinessType.valueOf(po.getBusinessType()).getDesc());
                vo.setBusinessOrderType(po.getBusinessOrderType());

                PlatformAccountBo platformAccountBo = null;
                if(!platformAccountBos.isEmpty()) {
                    platformAccountBo = platformAccountBos.stream().filter(item -> po.getBusinessOrderType().equals(item.getAccountType())).findAny().orElse(null);
                }
                vo.setBusinessOrderTypeName(platformAccountBo==null ? StringUtils.defaultString(po.getBusinessOrderType()) : platformAccountBo.getSettleName());
                vo.setAmount(po.getAmount());
                vo.setClientSerialNo(po.getClientSerialNo());
                vo.setFeeAmount(po.getFeeAmount());
                vo.setExtra(po.getExtra());
                vo.setFurunInfo(po.getFurunInfo());
                vo.setGuarantee(po.getGuarantee());
                vo.setInAccountName(po.getInAccountName());
                vo.setInAccountNo(po.getInAccountNo());
                vo.setInBankCode(po.getInBankCode());
                vo.setInCardNo(po.getInCardNo());
                vo.setMerchantNo(po.getMerchantNo());
                vo.setOutAccountName(po.getOutAccountName());
                vo.setOutAccountNo(po.getOutAccountNo());
                vo.setOutBankCode(po.getOutBankCode());
                vo.setOrderNo(po.getOrderNo());
                vo.setPayTime(sdfResult.format(po.getPayTime()));
                if("1".equals(po.getTradeStatus()) || "成功".equals(po.getTradeStatus())) {
                    vo.setSuccessTime(sdfResult.format(po.getSuccessTime()));
                }
                vo.setRemarks(po.getRemarks());
                vo.setPropateInfo(po.getPropateInfo());
                vo.setTradeStatus("1".equals(po.getTradeStatus()) || "成功".equals(po.getTradeStatus()) ? "成功":"未知");
                vo.setTradeType(po.getTradeType());
                Arrays.stream(TradeTypeEnum.values()).forEach(type -> {
                    if(type.getCode().equals(po.getTradeType())) {
                        vo.setTradeType(type.getDesc());
                    }
                });
                vos.add(vo);
            });
        }
        return vos;
    }

    private String buildFileName(FbankPayRecordsQueryParam param){
        StringBuffer sb = new StringBuffer();
        SimpleDateFormat sdfExport = new SimpleDateFormat("yyyyMMdd");
        sb.append(param.getPayStartTime()==null?"":sdfExport.format(param.getPayStartTime())+"-");
        sb.append(param.getPayEndTime()==null?"":sdfExport.format(param.getPayEndTime())+"-");
        sb.append(param.getBusinessType()==null?"":BusinessType.valueOf(param.getBusinessType()).getDesc()+"-");
        sb.append("对账.xlsx");
        return sb.toString();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        List<PlatformAccountBo> tmpAccounts =  JSONUtils.parse2List(ApolloUtil.getProperty("platform_accounts"), PlatformAccountBo.class);
        if(tmpAccounts == null) {
            log.error("TradeCheckServiceImpl#onChange change platformAccountBos  error");
            return;
        }
        platformAccountBos = tmpAccounts;

        Set set = new HashSet();
        set.add("platform_accounts");
        //动态监听配置变化
        Config config = ConfigService.getAppConfig();
        config.addChangeListener(new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
                List<PlatformAccountBo> tmpAccounts =  JSONUtils.parse2List(ApolloUtil.getProperty("platform_accounts"), PlatformAccountBo.class);
                if(tmpAccounts == null) {
                    log.error("TradeCheckServiceImpl#onChange change platformAccountBos  error");
                    return;
                }
                platformAccountBos = tmpAccounts;
                log.info("TradeCheckServiceImpl#onChange change platformAccountBos  success platformAccountBos:{} " , JSON.toJSONString(platformAccountBos));
                // 查询各个账户余额
                log.info("TradeCheckServiceImpl#refreshPlatFormData begin");
                for(PlatformAccountBo pb : platformAccountBos) {
                    QueryFuminAccountVo queryFuminAccountVo = new QueryFuminAccountVo();
                    queryFuminAccountVo.setUserId(pb.getAccountId());
                    pb.setResidual(String.valueOf(platformAccountsManage.queryAccountBalance(pb.getAccountId())));
                }
                log.info("TradeCheckServiceImpl#refreshPlatFormData end");
            }
        }, set);
    }
}
